using Abp.AspNetCore.Mvc.Authorization;
using Abp.Collections.Extensions;
using Abp.Extensions;
using Abp.Runtime.Session;
using Abp.Timing;
using Abp.UI;
using Abp.Web.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Mt.Site.Application;
using Mt.Site.Application.FileFinder;
using Mt.Site.Application.FileFinder.Dto;
using Mt.Site.Core.Base.BlobStoring;
using Mt.Site.Core.Base.Settings;
using Mt.Site.Core.Base.Utils.FileIO;
using Mt.Site.Core.Base.Utils.FileIO.Upload;
using Mt.Site.Core.Base.Utils.ImageProcess;
using Mt.Site.Core.CustomConfigurations;
using Mt.Site.Core.Organizations.UserCache;
using Mt.Site.Core.Security;
using Mt.Site.Core.Storage.File_;
using Mt.Site.Core.Storage.Permissions;
using Mt.Site.Web.Areas.B.Models.EditorFile;
using Mt.Site.Web.Controllers;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Mt.Site.Web.Areas.B.Controllers
{
    [Area("B")]
    public class EditorFileController : MtSiteControllerBase
    {
        private readonly IFileUpload _fileUpload;
        private readonly ICustomConfigManager _customConfigManager;
        private readonly IFileLibraryManager _fileLibraryManager;

        private readonly IMtConfiguration _mtConfiguration;
        private readonly IBlobContainer _container;
        private readonly IAppFolders _appFolders;
        private readonly IUserAndUnitFinder _userAndUnitFinder;

        private readonly FileLibraryUploadHelper _fileLibraryUploadHelper;

        private readonly IUploadStrategy _uploadStrategy; 

        public EditorFileController(
            IFileUpload fileUpload,
            IFileLibraryManager fileLibraryManager,
            ICustomConfigManager customConfigManager,
            IAppFolders appFolders,
            IBlobContainer container,
            IMtConfiguration mtConfiguration,
            IUserAndUnitFinder userAndUnitFinder,
            FileLibraryUploadHelper fileLibraryUploadHelper,
            IUploadStrategy uploadStrategy)
        {
            _fileUpload = fileUpload;
            _customConfigManager = customConfigManager;
            _fileLibraryManager = fileLibraryManager;

            _appFolders = appFolders;
            _container = container;
            _mtConfiguration = mtConfiguration;

            _userAndUnitFinder = userAndUnitFinder;
            _fileLibraryUploadHelper = fileLibraryUploadHelper;
            _uploadStrategy = uploadStrategy;
        }

        // Ckeditor 上传
        [AbpMvcAuthorize(FilePermissions.Pages_Library_Upload)]
        [DontWrapResult]
        public async Task<ActionResult> UploadFileForCkeditorAsync(string type, string CKEditorFuncNum)
        {
            UpfileTemplate.Para fileInfo = await GetFileUploadConfig(type);

            var upfileResult = await _fileUpload.UploadFile(fileInfo.UpFileInfo, subDirectory: Clock.Now.ToString("yyyy-MM-dd"), HttpContext, _uploadStrategy);
            if (upfileResult.IsOk)
            {
                // 不管是否是已经存在于文件库中都要再复制一条数据（否则文件库中找不到这个文件难免麻烦）
                await _fileLibraryManager.AddToFileLibraryAutoAsync(upfileResult.UploadFileInfo, upfileResult.ExName);

                if (CKEditorFuncNum.IsNullOrEmpty())  //拖拽上传or剪切板上传
                {
                    return Json(new SuccessOutput { fileName = upfileResult.UploadFileInfo.FileName, uploaded = 1, url = upfileResult.UploadFileInfo.FileUrl });
                }
                else
                {
                    Response.ContentType = "text/html";
                    return Content("<script type='text/javascript'>window.parent.CKEDITOR.tools.callFunction('" + CKEditorFuncNum + "','" + upfileResult.UploadFileInfo.FileUrl + "','');</script>");
                }
            }
            else
            {
                if (CKEditorFuncNum.IsNullOrEmpty())
                {
                    return Json(new ErrorOutput { error = new ErrorMsg { number = 109, message = upfileResult.ExMsg } });
                }
                else
                {
                    Response.ContentType = "text/html";
                    return Content("<script type='text/javascript'>window.parent.CKEDITOR.tools.callFunction('" + CKEditorFuncNum + "',''," + "'" + upfileResult.ExMsg + "');</script>");
                }
            }
        }

        // 文件库小文件上传
        [AbpMvcAuthorize(FilePermissions.Pages_Library_Upload)]
        public async Task<LibraryFileItem> LibraryFileUploadAsync(LibraryFileUploadInput input)
        {
            if (input.ReplaceFileId > 0)
            {
                if (!await CheckReplacePermissionAsync(input.ReplaceFileId))
                {
                    throw new UserFriendlyException(L("NoDirectoryOperationPermission"));
                }
            }

            // 检查是否可以秒传
            FileLibrary fileEntity;
            var fileIsExistResult = await _fileLibraryUploadHelper.CheckFileIsExist(_fileLibraryManager, input.FileHash);
            if (fileIsExistResult != null)
            {
                fileEntity = await _fileLibraryUploadHelper.SaveExistFileToLibraryAsync(
                    fileIsExistResult, input.ReplaceFileId, input.DirectoryId, input.FileName, _fileLibraryManager, _container);
                if (fileEntity != null)
                {
                    return await FileLibraryToFileItemAsync(fileEntity);
                }
            }

            var exName = (Path.GetExtension(input.FileName) + "").ToLower();

            if (!_mtConfiguration.FileDownAllow_TenantSide.Contains(exName))
            {
                throw new UserFriendlyException(L("File_Invalid_Type_Error", _mtConfiguration.FileDownAllow_TenantSide.JoinAsString("、")));
            }

            var saveParams = await _fileLibraryUploadHelper.CheckFileSaveParams(input.FileName, input.ReplaceFileId, _container, _fileLibraryManager, exName);
            IFormFile upFile = CheckFormFile();

            // 文件库的图片也可能需要添加水印等处理
            if (saveParams.IsImage)
            {
                UpfileTemplate.Para fileInfo = await GetFileUploadConfig("");
                if (fileInfo.UpFileInfo.ImageSets.Count > 0)
                {
                    fileInfo.UpFileInfo.AdapterInfo(_mtConfiguration);

                    var stream = new MemoryStream();

                    await upFile.CopyToAsync(stream);

                    var checker = new FileExNameChecker();
                    bool transparent = (checker.GetFileExName(stream) == "PNG");

                    // 图片加工处理
                    var ImageResult = await _fileUpload.GetProcessedPicture(fileInfo.UpFileInfo.ImageSets[0], stream, transparent);
                    if (ImageResult != null)
                    {
                        stream.Dispose();

                        using (ImageResult.Stream)
                        {
                            // 先检查文件是否已存在
                            input.FileHash = FileSha1Helper.GetSHA1Hash(ImageResult.Stream);

                            fileIsExistResult = await _fileLibraryUploadHelper.CheckFileIsExist(_fileLibraryManager, input.FileHash);
                            if (fileIsExistResult != null)
                            {
                                fileEntity = await _fileLibraryUploadHelper.SaveExistFileToLibraryAsync(
                                    fileIsExistResult, input.ReplaceFileId, input.DirectoryId, input.FileName, _fileLibraryManager, _container);
                                if (fileEntity != null)
                                {
                                    return await FileLibraryToFileItemAsync(fileEntity);
                                }
                            }

                            input.Dimensions = string.Format("{0}x{1}", ImageResult.Width, ImageResult.Height);

                            input.FileSize = ImageResult.Stream.Length;

                            ImageResult.Stream.Position = 0;
                            await _container.SaveAsync(saveParams.SavePath, ImageResult.Stream, true);
                        }
                    }
                    else
                    {
                        stream.Position = 0;
                        await _container.SaveAsync(saveParams.SavePath, stream, true);
                        stream.Dispose();
                    }

                    fileEntity = await _fileLibraryUploadHelper.SaveNewFileInfoToLibraryAsync(saveParams, input.FileName,
                        input.FileSize, input.FileHash, input.Dimensions, input.DirectoryId, _fileLibraryManager);

                    return await FileLibraryToFileItemAsync(fileEntity);
                }
            }

            await _container.SaveAsync(saveParams.SavePath, upFile.OpenReadStream(), true);

            fileEntity = await _fileLibraryUploadHelper.SaveNewFileInfoToLibraryAsync(saveParams, input.FileName,
                    input.FileSize, input.FileHash, input.Dimensions, input.DirectoryId, _fileLibraryManager);

            return await FileLibraryToFileItemAsync(fileEntity);
        }

        // 文件库文件分片上传
        [AbpMvcAuthorize(FilePermissions.Pages_Library_Upload)]
        public async Task<int> PartUploadAsync(LibraryPartUploadInput input)
        {
            IFormFile upFile = CheckFormFile();

            if(input.FileHash.Contains("/") || input.FileHash.Contains("\\"))
            {
                throw new UserFriendlyException("参数异常");
            }

            // 建立独立文件夹
            var dir = Path.Combine(_appFolders.TempFileDownloadFolder, input.FileHash);
            FileHelper.FolderCreateIfNotExists(dir);

            var exName = ".tdd";
            var nameLength = input.Count.ToString().Length;

            var fileName = Path.Combine(dir, input.Index.ToString().PadLeft(nameLength, '0') + exName);
            using (var fileStream = System.IO.File.Open(fileName, FileMode.Create, FileAccess.Write))
            {
                await upFile.OpenReadStream().CopyToAsync(fileStream);
                await fileStream.FlushAsync();
            }

            return input.Index;
        }


        private async Task<LibraryFileItem> FileLibraryToFileItemAsync(FileLibrary fileEntity)
        {
            var directoriesData = await _fileLibraryManager.DirectoriesRepository.GetAllListAsync(tt => tt.Id == fileEntity.DirectoryId);
            return await _fileLibraryUploadHelper.FileLibraryToFileItemAsync(
                directoriesData, AbpSession.GetUserId(), fileEntity, ObjectMapper, _userAndUnitFinder, _fileLibraryManager);
        }

        private async Task<bool> CheckReplacePermissionAsync(long id)
        {
            var fileEntity = await _fileLibraryManager.GetFileLibraryOrNullAsync(id);
            if (fileEntity == null)
            {
                return false;
            }

            var hasPermission = await IsGrantedAsync(FilePermissions.Pages_Library_Edit);
            if (!hasPermission)
            {
                hasPermission = await IsGrantedAsync(FilePermissions.Pages_Library_Edit_Self);

                if (!hasPermission)
                {
                    return false;
                }

                var dir = await _fileLibraryManager.GetDirectoriesOrNullAsync(fileEntity.DirectoryId);
                if (dir == null || !_fileLibraryManager.IsCanEdit(dir, AbpSession.GetUserId()))
                {
                    return false;
                }
            }

            return true;
        }


        private IFormFile CheckFormFile()
        {
            if (!Request.HasFormContentType)
            {
                throw new UserFriendlyException(L("NoFilesSelected"));
            }
            var Formfiles = Request.Form.Files;

            if (Formfiles.Count == 0)
            {
                throw new UserFriendlyException(L("NoFilesSelected"));
            }
            return Formfiles[0];
        }

        private async Task<UpfileTemplate.Para> GetFileUploadConfig(string type)
        {
            int index = 0;  //Images
            if (type == "Files")
            {
                index = 1;
            }

            UpfileTemplate upfileTemplate = await _customConfigManager.GetUploadConfigurationAsync("ckeditor");
            var fileInfo = upfileTemplate.ModuleList[index];   //上传配置
            return fileInfo;
        }
    }
}
